// This file contains material supporting section 3.7 of the textbook:
// "Object Oriented Software Engineering" and is issued under the open-source
// license found at www.lloseng.com 

package client;

import java.io.IOException;
import java.util.Observable;
import java.util.Observer; //import java.util.StringTokenizer;
import ocsf.client.ObservableClient;
import common.ChatIF;

/**
 * This class overrides some of the methods defined in the abstract superclass
 * in order to give more functionality to the client. Phase 3 du projet
 * simplechat
 * 
 * @author Dr Timothy C. Lethbridge
 * @author Dr Robert Lagani&egrave;
 * @author Wagner Philippe
 * @version Mars 2010
 */
public class ChatClient extends ObservableClient implements Observer {
	// Instance variables **********************************************

	/**
	 * The interface type variable. It allows the implementation of the display
	 * method in the client.
	 */
	private ChatIF clientUI;
	private String loginID;


	// Constructors ****************************************************

	/**
	 * Constructs an instance of the chat client.
	 * 
	 * @param loginID
	 *            ou username
	 * @param host
	 *            The server to connect to.
	 * @param port
	 *            The port number to connect on.
	 * @param clientUI
	 *            The interface type variable.
	 */
	public ChatClient(String loginID, String host, int port, ChatIF clientUI)
			throws IOException {
		super(host, port);
		this.addObserver(this);

		this.clientUI = clientUI;
		this.loginID = loginID;
		try {
			this.openConnection();
			this.sendToServer("#login " + loginID);
		} catch (IOException e) {
			clientUI.display("Cannot open connection. Awaiting command.");
		}
	}

	// Instance methods ************************************************

	/**
	 * This method handles all data that comes in from the server.
	 * 
	 * @param msg
	 *            The message from the server.
	 */
	public void handleMessageFromServer(Object msg) {
		clientUI.display(msg.toString());
	}

	/**
	 * This method handles all data coming from the UI
	 * 
	 * @param message
	 *            The message from the UI.
	 */
	public void handleMessageFromClientUI(String message) {
		if (message.length() > 0 && message.charAt(0) == '#') {
			clientCommand(message);
		} else if (this.isConnected()) {
			try {
				this.sendToServer(message);
			} catch (IOException e) {
				clientUI.display("Could not send message to server.");
			}
		} else {
			clientUI.display("Not connected to server");
		}
	}

	/**
	 * This method terminates the client.
	 */
	public void quit() {
		try {
			this.closeConnection();
		} catch (IOException e) {
		}
		System.exit(0);
	}

	/**
	 * This method processes the message when there is a # as the first
	 * character.
	 * 
	 * @param command
	 *            The command to process
	 */
	public void clientCommand(String command) {

		if (command.equalsIgnoreCase("#quit")) {
			quit();
		} else if (command.equalsIgnoreCase("#logoff")) {
			try {
				this.closeConnection();
			} catch (IOException e) {
			}
		} else if (command.equalsIgnoreCase("#login")) {
			if (this.isConnected()) {
				clientUI.display("Already logged in");
			} else {
				clientUI.display("logging in");
				try {
					this.openConnection();
					this.sendToServer("#login " + loginID);
				} catch (IOException e) {
					clientUI.display("Unable to establish a"
							+ " connection to the host "
							+ this.getHost() + " on port "
							+ this.getPort());
				}
			}
		} else if (command.startsWith("#login ")) {
			if (this.isConnected()) {
				clientUI.display("You must be logged off to do that");
			} else {
				try {
					String newloginID = command.substring(7, command.length());
					loginID = newloginID;
					this.openConnection();
					this.sendToServer("#login " + loginID);
				} catch (IOException e) {
					clientUI.display("Unable to establish a"
							+ " connection to the host "
							+ this.getHost() + " on port "
							+ this.getPort());
				}
			}
		} else if (command.equalsIgnoreCase("#gethost"))
			clientUI.display("" + this.getHost());
		else if (command.equalsIgnoreCase("#getport"))
			clientUI.display("" + this.getPort());
		// On a mis un espace apr�s sethost pour tester si un host est donn�
		else if (command.startsWith("#sethost ")) {
			// contrainte : sethost se fait avant la connexion
			if (this.isConnected()) {
				clientUI.display("You must be logged off to do that");
			} else {
				String tempHost = command.substring(9, command.length());
				this.setHost(tempHost);
				clientUI.display("Host set to " + tempHost);
			}
		}
		// On a mis un espace apr�s sethost pour tester si un host est donn�
		else if (command.startsWith("#setport ")) {
			// contrainte : setport se fait avant la connexion
			if (this.isConnected()) {
				clientUI.display("You must be logged off to do that");
			} else {
				String tempPort = command.substring(9, command.length());
				// On test si c'est un chiffre
				try {
					this.setPort(Integer.parseInt(tempPort));
					clientUI.display("Port set to " + tempPort);
				} catch (NumberFormatException e) {
					clientUI.display("Port must be a number");
				}
			}
		} else if (command.startsWith("#help")) {
			// commande d'aide
			this.commandHelp(command);
		}
		// commandes inexistantes
		else {
			clientUI.display("Invalid command");
			clientUI.display("Please use #help for command help");
		}

	}

	/**
	 * Methode qui permet d'afficher l'aide
	 * 
	 * @param command
	 *            commande pour laquelle on demande de l'aide
	 */
	private void commandHelp(String command) {
		// Cas #help #help
		if (command.equalsIgnoreCase("#help")) {
			clientUI.display("Commands\tdescription");
			clientUI.display("#quit\t\texit client program");
			clientUI.display("#logoff\tdisconnect from server");
			clientUI.display("#login\tused to connect to server");
			clientUI
					.display("#gethost\treturns hostname of host you are connected to");
			clientUI
					.display("#getport\treturns port number you are connected to");
			clientUI
					.display("#sethost\tif you are not logged in you may specify the host to log into");
			clientUI
					.display("#setport\tif you are not logged in you may specify the port number to connect to");
			clientUI.display("#help\t\tthis menu");
		} else if (command.equalsIgnoreCase("#help #quit")) {
			clientUI.display("Usage:\t#quit");
		} else if (command.equalsIgnoreCase("#help #logoff")) {
			clientUI.display("Usage:\t#logoff");
		} else if (command.equalsIgnoreCase("#help #login")) {
			clientUI.display("Usage:\t#login loginID");
		} else if (command.equalsIgnoreCase("#help #gethost")) {
			clientUI.display("Usage:\t#gethost");
		} else if (command.equalsIgnoreCase("#help #getport")) {
			clientUI.display("Usage:\t#getport");
		} else if (command.equalsIgnoreCase("#help #sethost")) {
			clientUI.display("Usage:\t#sethost <hostname>");
		} else if (command.equalsIgnoreCase("#help #setport")) {
			clientUI.display("Usage:\t#setport <portname>");
		} else
			clientUI.display("invalid help request");
	}

	/**
	 * Appel� apr�s la fermeture de la connexion
	 */
	public void connectionClosed() {
		clientUI.display("Connection to " + this.getHost()
				+ " closed.");
	}

	/**
	 * @param exception
	 *            Definit l'exception lanc�e
	 */
	public void connectionException(Exception exception) {
		clientUI
				.display("Connection to " + this.getHost() + " lost.");
	}

	/**
	 * Methode appel�e de apr�s que la connexion a �t� �tablie
	 */
	public void connectionEstablished() {
		clientUI.display("Welcome! You are connected to "
				+ this.getHost());
	}

	/**
	 * Chap6 Florent & Mejdi: update() est appelé automatiquement pour
	 * déclencher des actions, quand les serveurs appellent la méthode
	 * notifyObservers()
	 * 
	 * Il faut donc traiter ce qu'on recoit pour savoir ce que c'est message de
	 * connection / exception / commande ...
	 */
	@Override
	public void update(Observable o, Object arg) {
		// ObservableServer server = (ObservableServer) o;

		if (o == this) {

			if (arg instanceof String) {
				if (arg == this.CONNECTION_CLOSED)
					connectionClosed();
				else if (arg == this.CONNECTION_ESTABLISHED)
					connectionEstablished();
				else
					handleMessageFromServer(arg);

			} else if (arg instanceof Exception) {
				connectionException((Exception) arg);
			}

		}
	}
}
// End of ChatClient class